# Copyright (c) Microsoft. All rights reserved.

from typing import TYPE_CHECKING, List, Optional, Protocol, runtime_checkable

from semantic_kernel.orchestration.context_variables import ContextVariables
from semantic_kernel.template_engine.blocks.block import Block

if TYPE_CHECKING:
    from semantic_kernel.orchestration.sk_context import SKContext


@runtime_checkable
class PromptTemplatingEngine(Protocol):
    """
    Prompt templating engine protocol.
    """

    def extract_blocks(
        self, template_text: Optional[str] = None, validate: bool = True
    ) -> List[Block]:
        """
        Given a prompt template string, extract all the blocks
        (text, variables, function calls).

        :param template_text: Prompt template (see skprompt.txt files)
        :param validate: Whether to validate the blocks syntax, or just
            return the blocks found, which could contain invalid code
        :return: A list of all the blocks, ie the template tokenized in
            text, variables and function calls
        """
        ...

    async def render_async(self, template_text: str, context: "SKContext") -> str:
        """
        Given a prompt template, replace the variables with their values
        and execute the functions replacing their reference with the
        function result.

        :param template_text: Prompt template (see skprompt.txt files)
        :param context: Access into the current kernel execution context
        :return: The prompt template ready to be used for an AI request
        """
        ...

    async def render_blocks_async(
        self, blocks: List[Block], context: "SKContext"
    ) -> str:
        """
        Given a list of blocks render each block and compose the final result.

        :param blocks: Template blocks generated by ExtractBlocks
        :param context: Access into the current kernel execution context
        :return: The prompt template ready to be used for an AI request
        """
        ...

    def render_variables(
        self, blocks: List[Block], variables: Optional[ContextVariables] = None
    ) -> List[Block]:
        """
        Given a list of blocks, render the Variable Blocks, replacing
        placeholders with the actual value in memory.

        :param blocks: List of blocks, typically all the blocks found in a template
        :param variables: Container of all the temporary variables known to the kernel
        :return: An updated list of blocks where Variable Blocks have rendered to
            Text Blocks
        """
        ...

    async def render_code_async(
        self, blocks: List[Block], execution_context: "SKContext"
    ) -> List[Block]:
        """
        Given a list of blocks, render the Code Blocks, executing the
        functions and replacing placeholders with the functions result.

        :param blocks: List of blocks, typically all the blocks found in a template
        :param execution_context: Access into the current kernel execution context
        :return: An updated list of blocks where Code Blocks have rendered to
            Text Blocks
        """
        ...
